package com.yblinag.plat.common.module.mybatis.interceptor;

import com.ybliang.plat.common.data.vo.req.PageReqVo;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author liangyb1
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
})
public class PagingInterceptor implements Interceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(PagingInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler   = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler     = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement     = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");

        //获取参数
        BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
        Object parameterObject = boundSql.getParameterObject();

        /**
         * 判断是否是继承PageReqVo来判断是否需要进行分页
         */
        if (parameterObject instanceof PageReqVo) {
            //强转 为了拿到分页数据
            PageReqVo page = (PageReqVo) parameterObject;
            String sql = boundSql.getSql();

            String countSql=concatCountSql(sql);
            String pageSql=concatPageSql(sql, page);

            Connection connection = (Connection) invocation.getArgs()[0];

            //获取相关配置
            Configuration config = mappedStatement.getConfiguration();
            PreparedStatement preparedStatement = null;
            ResultSet rs = null;
            int total = 0;
            try {
                preparedStatement = connection.prepareStatement(countSql);
                BoundSql countBoundSql = new BoundSql(config, countSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
                ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBoundSql);
                parameterHandler.setParameters(preparedStatement);
                rs = preparedStatement.executeQuery();
                if (rs.next()) {
                    total = rs.getInt(1);
                }

            } catch (SQLException e) {
                LOGGER.error("paging interceptor error...", e);
            } finally {
                try {
                    if(null != rs) { rs.close(); }
                    if(null != preparedStatement) { preparedStatement.close(); }
                } catch (SQLException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }

            //绑定count
            page.setTotal(total);
            metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);

        }

        Object proceed = null;
        long start = 0L;
        try {
            start = System.currentTimeMillis();
            proceed = invocation.proceed();
        } finally {
            long cost   = System.currentTimeMillis() - start;
            if(cost >= 100L) {
                String sql = ((BoundSql) metaStatementHandler.getValue("delegate.boundSql")).getSql().replaceAll("\\s*", "");
                LOGGER.warn("{} cost:{}ms, sql:{}", mappedStatement.getId(), cost, sql);
            }
        }

        return proceed;

    }

    public String concatCountSql(String sql){
        StringBuffer sb = new StringBuffer("select count(*) from ");
        sql=sql.toLowerCase();

        if(sql.lastIndexOf("order")>sql.lastIndexOf(")")){
            sb.append(sql.substring(sql.indexOf("from")+4, sql.lastIndexOf("order")));
        }else{
            sb.append(sql.substring(sql.indexOf("from")+4));
        }
        return sb.toString();
    }

    public String concatPageSql(String sql, PageReqVo reqVo){
        StringBuffer sb = new StringBuffer();
        sb.append(sql);
        int start = (reqVo.getPageNo() - 1) * reqVo.getPageSize();
        sb.append(" limit ").append(start).append(" , ").append(reqVo.getPageSize());
        return sb.toString();
    }


    @Override
    public Object plugin(Object o) {
        Object wrap = Plugin.wrap(o, this);
        return wrap;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
